#import <UIKit/UIApplication.h>
#import <objc/runtime.h>
#import <LockInfo/Plugin.h>

#import "framework/FWCommon.h"

#import "PICalTimeSummary.h"
#import "PICalListAccess.h"


@implementation PICalTimeSummary

@synthesize timeIntervals, legendColor, borderColor, scaleColor, start, duration, timeRangeOnly;


// ******************************************************************************************
#pragma mark -
#pragma mark Initialisation and object management

- (id)initWithFrame:(CGRect)frame
{
    if ((self = [super initWithFrame:frame])) {
        start = 0;
        duration = SECONDS_PER_DAY;
        timeRangeOnly = NO;
    }
    
    return self;
}

- (void)dealloc
{
    [timeIntervals release];
    [legendColor release];
    [scaleColor release];
    [borderColor release];
    
    [super dealloc];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Pattern drawing support

static NSInteger const kStripeWidth = 25;
static NSInteger const kStripeHeight = 10;
static CGFloat const kStripeRotate = 3.1415 / 5;

void drawStripes(void *info, CGContextRef context)
{
    NSArray* colors = (NSArray*)info;
    for (NSInteger i = 0; i < colors.count; ++i) {
        CGContextSetFillColorWithColor(context, [[colors objectAtIndex:i] CGColor]);
        CGContextFillRect(context, CGRectMake(kStripeWidth * i, 0, kStripeWidth + 1, kStripeHeight + 1));
    }
}

void releaseStripes(void *info)
{
    // Release the colours array now that we've finished filling
    NSArray* colors = (NSArray*)info;
    [colors release];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Rectangle drawing

- (void)drawFilledRect:(CGRect)rect context:(CGContextRef)context border:(UIColor*)border fill:(BOOL)fill
{
    CGContextBeginPath(context);
    
    CGContextSetLineWidth(context, 1);
    CGContextSetStrokeColorWithColor(context, border.CGColor);
    
    CGFloat segRadius = 4;
    
    CGFloat minx = CGRectGetMinX(rect);
    CGFloat midx = CGRectGetMidX(rect);
    CGFloat maxx = CGRectGetMaxX(rect);
    CGFloat miny = CGRectGetMinY(rect);
    CGFloat midy = CGRectGetMidY(rect);
    CGFloat maxy = CGRectGetMaxY(rect);
    
    CGContextMoveToPoint(context, midx, miny);
    CGContextAddArcToPoint(context, maxx, miny, maxx, midy, segRadius);
    CGContextAddArcToPoint(context, maxx, maxy, midx, maxy, segRadius);
    CGContextAddArcToPoint(context, minx, maxy, minx, midy, segRadius);
    CGContextAddArcToPoint(context, minx, miny, midx, miny, segRadius);
    CGContextClosePath(context);
    
    CGContextDrawPath(context, fill ? kCGPathFillStroke : kCGPathStroke);
}

- (BOOL)drawFilledRect:(CGRect)rect context:(CGContextRef)context colors:(NSArray*)colors border:(UIColor*)border
{
    if (rect.size.width == 0)
        return NO;
    
    // Retain the colours so that the fill function can use them. It will release
    // them when it is finished
    [colors retain];
    
    CGContextSaveGState(context);
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreatePattern(NULL);
    CGContextSetFillColorSpace(context, colorSpace);
    CGColorSpaceRelease(colorSpace);

    CGFloat alpha = 1.0;
    CGPatternCallbacks callback = { 0, &drawStripes, &releaseStripes };
    CGAffineTransform tr = CGAffineTransformRotate(CGAffineTransformIdentity, kStripeRotate);

    CGPatternRef pattern = CGPatternCreate(colors,
                                           CGRectMake(0, 0, colors.count * kStripeWidth, kStripeHeight),
                                           tr,
                                           colors.count * kStripeWidth, kStripeHeight,
                                           kCGPatternTilingConstantSpacingMinimalDistortion,
                                           true,
                                           &callback);
    CGContextSetFillPattern(context, pattern, &alpha);
    CGPatternRelease(pattern);
    
    [self drawFilledRect:rect context:context border:border fill:YES];
    
    CGContextRestoreGState(context);
    
    return YES;
}

- (BOOL)drawFilledRect:(CGRect)rect context:(CGContextRef)context color:(UIColor*)color border:(UIColor*)border dotted:(BOOL)dotted shadow:(BOOL)shadow
{
    if (rect.size.width == 0)
        return NO;
    
    CGContextSaveGState(context);
    
    if (shadow) {
        UIColor* shadowColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
        CGContextSetFillColorWithColor(context, shadowColor.CGColor);
        CGFloat shadowIndent = 2;
        
        CGRect shadowRect = CGRectMake(rect.origin.x + shadowIndent, rect.origin.y + shadowIndent,
                                       rect.size.width, rect.size.height);
        [self drawFilledRect:shadowRect context:context border:shadowColor fill:YES];
    }

    CGContextSetFillColorWithColor(context, color.CGColor);

    if (dotted) {
        CGFloat dashArray[] = {5, 2};
        CGContextSetLineDash(context, 0, dashArray, sizeof(dashArray) / sizeof(CGFloat));    
    }
    
    [self drawFilledRect:rect context:context border:border fill:![color isEqual:[UIColor clearColor]]];
    CGContextRestoreGState(context);
    
    return YES;
}

- (void)drawScale:(CGRect)rect context:(CGContextRef)context
{
    CGContextSetLineWidth(context, 1.5);

    CGContextSetStrokeColorWithColor(context, legendColor.CGColor);
    
    CGFloat dashArray[] = {1, 1};

    CGContextSetLineDash(context, 0, dashArray, sizeof(dashArray) / sizeof(CGFloat));
    CGContextSetBlendMode(context, kCGBlendModeExclusion);
    
    CGFloat minx = CGRectGetMinX(rect);
    CGFloat maxx = CGRectGetMaxX(rect);
    CGFloat miny = CGRectGetMinY(rect);
    CGFloat maxy = CGRectGetMaxY(rect);
    CGFloat scale = rect.size.width / duration;
    CGFloat hourIndent = rect.size.height / 3;
    CGFloat divideIndent = rect.size.height / 4;
    NSInteger kSkipDuration = 12 * SECONDS_IN_HOUR;
    
    for (NSInteger hour = 0; hour < 24; ++hour) {
        NSInteger pos = (hour * 3600) - start;
        if (pos >= 0) {
            pos = minx + pos * scale;
            if (pos <= maxx) {
                // Don't draw too close to the ends - it looks bad
                if (((pos - minx) <= 2) || ((maxx - pos) <= 2))
                    continue;
                
                if (hour == 12) {
                    // Draw the midday marker
                    CGContextMoveToPoint(context, pos, miny);
                    CGContextAddLineToPoint(context, pos, maxy);
                }
                else if ((hour % 2) == 0) {
                    // Draw a time divider
                    CGContextMoveToPoint(context, pos, miny + divideIndent);
                    CGContextAddLineToPoint(context, pos, maxy - divideIndent);
                }
                else {
                    // Draw an hour marker
                    CGContextMoveToPoint(context, pos, miny + hourIndent);
                    CGContextAddLineToPoint(context, pos, maxy - hourIndent);
                }
            }
        }
        
        if (duration > kSkipDuration)
            ++hour;
    }
    
    CGContextDrawPath(context, kCGPathStroke);
}

- (CGRect)rectForStart:(NSInteger)begin withDuration:(NSInteger)length withinRect:(CGRect)target
{
    NSInteger periodEnd = start + duration;
    NSInteger myEnd = begin + length;
    
    // If outside the period then nothing to draw
    if ((begin >= periodEnd) || (myEnd <= start))
        return CGRectZero;
    
    if (begin < start) {
        length -= start - begin;
        begin = 0;
    }
    else
        begin -= start;
    
    if (myEnd > periodEnd)
        length -= myEnd - periodEnd;
    
    CGFloat perSec = target.size.width / duration;
    
    target.origin.x += begin * perSec;
    target.size.width = length * perSec;
    
    return target;
}


// ******************************************************************************************
#pragma mark -
#pragma mark Drawing support

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];

    // Take our own copy of the time intervals in case they change whilst we are updating
    NSArray* localTimeIntervals = [timeIntervals retain];
    
    BOOL itemsDrawn = NO;
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGRect summaryRect = CGRectInset(self.bounds, 1, 1);    // Rectangle for the outer display
    CGRect fullRangeRect = CGRectInset(summaryRect, 1, 1);  // Rectangle for a full-range item
    CGRect allDayRect = CGRectInset(fullRangeRect, 2, 2);   // Rectangle for all-day activities

    // Draw the surround
    [self drawFilledRect:summaryRect context:context color:scaleColor border:borderColor dotted:YES shadow:NO];
    
    // Get the colors of the all-day activities
    NSMutableArray* allDayColors = [NSMutableArray arrayWithCapacity:5];
    for (PICalListAccessRange* range in localTimeIntervals) {
        if (range.isAllDay) {
            if (range.color)
                [allDayColors addObject:range.color];
        }
        else
            itemsDrawn |= ([self rectForStart:range.start
                                 withDuration:range.duration
                                   withinRect:summaryRect].size.width > 0);
    }

    if (allDayColors.count == 1)
        [self drawFilledRect:allDayRect
                     context:context
                       color:[allDayColors objectAtIndex:0]
                      border:[UIColor blackColor]
                      dotted:NO
                      shadow:NO];
    else if (allDayColors.count > 1)
        [self drawFilledRect:allDayRect
                     context:context
                      colors:allDayColors
                      border:[UIColor blackColor]];

    
    for (PICalListAccessRange* range in localTimeIntervals) {
        if (range.duration >= 0) {
            CGRect drawRect = [self rectForStart:range.start withDuration:range.duration withinRect:fullRangeRect];
            itemsDrawn |= [self drawFilledRect:drawRect
                                       context:context
                                         color:range.color 
                                        border:[UIColor blackColor] 
                                        dotted:NO
                                        shadow:YES];
        }
    }
    
    [self drawScale:fullRangeRect context:context];
    
    [localTimeIntervals release];
}

@end
